{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fead49e87f357db",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-08-18T07:06:11.835351Z",
     "start_time": "2025-08-18T07:06:11.833008Z"
    }
   },
   "outputs": [],
   "source": [
    "import os\n",
    "os.environ[\"HF_ENDPOINT\"] = \"https://hf-mirror.com\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "initial_id",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-08-18T07:06:14.255492Z",
     "start_time": "2025-08-18T07:06:13.643104Z"
    },
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from dotenv import load_dotenv\n",
    "load_dotenv()\n",
    "import getpass\n",
    "import os\n",
    "\n",
    "if not os.environ.get(\"DEEPSEEK_API_KEY\"):\n",
    "  os.environ[\"DEEPSEEK_API_KEY\"] = getpass.getpass(\"Enter API key for Google Gemini: \")\n",
    "\n",
    "from langchain.chat_models import init_chat_model\n",
    "\n",
    "llm = init_chat_model(\"deepseek-chat\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "b657a345d5885178",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-08-18T07:06:26.450466Z",
     "start_time": "2025-08-18T07:06:16.442882Z"
    }
   },
   "outputs": [],
   "source": [
    "import os\n",
    "os.environ[\"HF_ENDPOINT\"] = \"https://hf-mirror.com\"\n",
    "from langchain_huggingface import HuggingFaceEmbeddings\n",
    "\n",
    "# embeddings = HuggingFaceEmbeddings(model_name=\"sentence-transformers/all-mpnet-base-v2\")\n",
    "embeddings = HuggingFaceEmbeddings(model_name=\"Qwen/Qwen3-Embedding-0.6B\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2f9bac6aa1522bd1",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-08-18T07:04:14.107678Z",
     "start_time": "2025-08-18T07:04:13.292429Z"
    }
   },
   "outputs": [],
   "source": [
    "\n",
    "from sentence_transformers import SentenceTransformer\n",
    "\n",
    "# Load the model\n",
    "model = SentenceTransformer(\"Qwen/Qwen3-Embedding-0.6B\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f7afb04ae4cd1289",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-08-18T07:06:34.384860Z",
     "start_time": "2025-08-18T07:06:34.358281Z"
    }
   },
   "outputs": [],
   "source": [
    "from langchain_core.vectorstores import InMemoryVectorStore\n",
    "\n",
    "vector_store = InMemoryVectorStore(embeddings)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e47f990852948dec",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-08-18T07:15:37.900065Z",
     "start_time": "2025-08-18T07:15:37.292331Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[Document(metadata={'source': 'https://blog.csdn.net/python12222_/article/details/145327194'}, page_content='\\n\\n\\n\\n大模型之RAG-基于向量检索的理论与实战，对比关键字检索方案\\n\\n\\n最新推荐文章于\\xa02025-07-04 16:06:08\\xa0发布\\n\\n\\n原创\\n最新推荐文章于\\xa02025-07-04 16:06:08\\xa0发布\\n·\\n1.6k 阅读\\n\\n·\\n\\n\\n\\n                      12\\n                  \\n\\n·\\n\\n\\n\\n\\n                      13\\n                  \\n\\n·\\n\\nCC 4.0 BY-SA版权\\n\\n                                版权声明：本文为博主原创文章，遵循 CC 4.0 BY-SA 版权协议，转载请附上原文出处链接和本声明。\\n                    \\n\\n\\n\\n\\n\\n\\n\\n\\n文章标签：\\n\\n#机器学习\\n#深度学习\\n#算法\\n#人工智能\\n#AI大模型\\n#RAG\\n#大模型应用\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n前言\\nRAG系列的讲解，我们之前和大家分享了RAG的流程、文档切分、基于关键字检索的方案。\\n今天来和大家一起分享基于向量检索的方案与实战，再结合关键字检索方案做一下多维度的对比。 让我们对RAG的实现方案能够加深一些理解，在面对不同场景中，选择合适的方案。\\n\\n向量检索的定义与原理\\n什么是向量\\n向量是一种有大小和方向的数学对象。它可以表示为从一个点到另一个点的有向线段。例如，二维空间中的向量可以表示为 (𝑥,𝑦)(𝑥,𝑦)，表示从原点 (0,0)(0,0) 到点 (𝑥,𝑦)(𝑥,𝑦) 的有向线段。\\n\\n以此类推，我可以用一组坐标 (𝑥0,𝑥1,…,𝑥𝑁−1)(𝑥0,𝑥1,…,𝑥𝑁−1) 表示一个 𝑁𝑁 维空间中的向量，𝑁𝑁 叫向量的维度。\\n文本向量（Text Embeddings）\\n将文本转成一组 𝑁𝑁 维浮点数，即文本向量又叫 Embeddings向量之间可以计算距离，距离远近对应语义相似度大小\\n\\n文本向量是怎么得到的\\n构建相关（正立）与不相关（负例）的句子对儿样本训练双塔式模型，让正例间的距离小，负例间的距离大\\n\\n向量间的相似度计算\\n\\n我们用检索关键词和一组文本的样例来看下效果\\n余弦距离和欧氏距离的核心逻辑\\ndef cos_sim(a, b):\\n    \\'\\'\\'余弦距离 -- 越大越相似\\'\\'\\'\\n    return dot(a, b)/(norm(a)*norm(b))\\n\\ndef l2(a, b):\\n    \\'\\'\\'欧氏距离 -- 越小越相似\\'\\'\\'\\n    x = np.asarray(a)-np.asarray(b)\\n    return norm(x)\\n    \\ndef get_embeddings(texts, model=\"text-embedding-ada-002\", dimensions=None):\\n    \\'\\'\\'封装 OpenAI 的 Embedding 模型接口\\'\\'\\'\\n    if model == \"text-embedding-ada-002\":\\n        dimensions = None\\n    if dimensions:\\n        data = client.embeddings.create(\\n            input=texts, model=model, dimensions=dimensions).data\\n    else:\\n        data = client.embeddings.create(input=texts, model=model).data\\n    return [x.embedding for x in data]\\n    \\n# query = \"国际争端\"\\n\\n# 且能支持跨语言\\nquery = \"global conflicts\"\\n\\ndocuments = [\\n    \"联合国就苏丹达尔富尔地区大规模暴力事件发出警告\",\\n    \"土耳其、芬兰、瑞典与北约代表将继续就瑞典“入约”问题进行谈判\",\\n    \"日本岐阜市陆上自卫队射击场内发生枪击事件 3人受伤\",\\n    \"国家游泳中心（水立方）：恢复游泳、嬉水乐园等水上项目运营\",\\n    \"我国首次在空间站开展舱外辐射生物学暴露实验\",\\n]\\n    \\n\\n\\n执行并输出结果\\nquery_vec = get_embeddings([query])[0]\\ndoc_vecs = get_embeddings(documents)\\n\\nprint(\"Query与自己的余弦距离: {:.2f}\".format(cos_sim(query_vec, query_vec)))\\nprint(\"Query与Documents的余弦距离:\")\\nfor vec in doc_vecs:\\n    print(cos_sim(query_vec, vec))\\n\\nprint()\\n\\nprint(\"Query与自己的欧氏距离: {:.2f}\".format(l2(query_vec, query_vec)))\\nprint(\"Query与Documents的欧氏距离:\")\\nfor vec in doc_vecs:\\n    print(l2(query_vec, vec))\\n\\n\\n我们来看下执行的效果：\\nQuery与自己的余弦距离: 1.00\\nQuery与Documents的余弦距离:\\n0.7622749944010915（越大越相似）\\n0.7563038106493584\\n0.7426665802579038\\n0.7079273699608006\\n0.7254355321045072\\n\\nQuery与自己的欧氏距离: 0.00\\nQuery与Documents的欧氏距离:\\n0.6895288502682277（越小越相似）\\n0.6981349637998769\\n0.7174028746492277\\n0.7642939833636829\\n0.7410323668625171\\n\\n\\n向量数据库\\n向量数据库（Vector Database），也叫矢量数据库，主要用来存储和处理向量数据。\\n再结合刚才我们对向量定义的描述，图像、文本和音视频这种非结构化数据都可以通过某种变换或者嵌入学习转化为向量数据存储到向量数据库中，从而实现对图像、文本和音视频的相似性搜索和检索。\\n这意味着您可以使用向量数据库根据语义或上下文含义查找最相似或相关的数据，而不是使用基于精确匹配或预定义标准查询数据库的传统方法。也就是我们提到的关键字检索的局限性。\\n向量数据库的特点\\n这里我们为了方便使用向量数据库完成向量检索的方案，简单介绍下向量数据库的特点：\\n向量数据库的主要特点是高效存储与检索。利用索引技术和向量检索算法能实现高维大数据下的快速响应。向量数据库也是一种数据库，除了要管理向量数据外，还是支持对传统结构化数据的管理。实际使用时，有很多场景会同时对向量字段和结构化字段进行过滤检索，这对向量数据库来说也是一种挑战。\\n严格来说数据向量化本不属于向量数据库，但是数据向量化又是一项很重要的工作，为了流程的完整性暂且放进去。区别与传统数据库主要有以下几个地方不相同：数据向量化，向量检索和相似度计算。\\nchromadb的简单介绍\\n之所以介绍一下chromadb，下面我们的实战demo就是基于chromadb来实现。\\nChroma的目标是帮助用户更加便捷地构建大模型应用，更加轻松的将知识（knowledge）、事实（facts）和技能（skills）等我们现实世界中的文档整合进大模型中。\\nChroma提供的工具：\\n存储文档数据和它们的元数据：store embeddings and their metadata嵌入：embed documents and queries搜索： search embeddings\\n流向量数据库功能对比\\n由于大模型的火热，现在市面上的向量数据库众多，主流的向量数据库对比如下所示：\\n向量数据库URLGitHub StarLanguagechromagithub.com/chroma-core…7.4KPythonmilvusgithub.com/milvus-io/m…21.5KGo/Python/C++pineconewww.pinecone.io/❌❌qdrantgithub.com/qdrant/qdra…11.8KRusttypesensegithub.com/typesense/t…12.9KC++weaviategithub.com/weaviate/we…6.9KGo\\n\\n表格引用自：一文全面了解向量数据库的基本概念、原理、算法、选型\\n\\n一个基于文档向量检索的RAG实战例子\\n我们再回顾RAG的基本流程，对照如下例子，大家就可以更好理解了\\nRAG系统搭建的基本流程\\n准备对应的垂域资料文档的读取解析，进行文档切分将分割好的文本灌入检索引擎（向量数据库）封装检索接口构建流程：Query -> 检索 -> Prompt -> LLM -> 回复文档加载\\ndef extract_text_from_pdf(filename,page_numbers=None,min_line_length=10):\\n    \"\"\"从 PDF 文件中（按指定页码）提取文字\"\"\"\\n    paragraphs = []\\n    buffer = \\'\\'\\n    full_text = \\'\\'\\n    # 提取全部文本\\n    for i, page_layout in enumerate(extract_pages(filename)):\\n        # 如果指定了页码范围，跳过范围外的页\\n        if page_numbers is not None and i not in page_numbers:\\n            continue\\n        for element in page_layout:\\n            if isinstance(element, LTTextContainer):\\n                full_text += element.get_text() + \\'\\\\n\\'\\n    # 按空行分隔，将文本重新组织成段落\\n    lines = full_text.split(\\'\\\\n\\')\\n    for text in lines:\\n        if len(text) >= min_line_length:\\n            buffer += (\\' \\'+text) if not text.endswith(\\'-\\') else text.strip(\\'-\\')\\n        elif buffer:\\n            paragraphs.append(buffer)\\n            buffer = \\'\\'\\n    if buffer:\\n        paragraphs.append(buffer)\\n    return paragraphs\\n\\n\\n文档切割（交叠切割防止问题的答案跨两个片段，使上下文更完整）\\ndef split_text(paragraphs,chunk_size=300,overlap_size=100):\\n    \"\"\"按指定 chunk_size 和 overlap_size 交叠割文本\"\"\"\\n    sentences = [s.strip() for p in paragraphs for s in sent_tokenize(p)]\\n    chunks = []\\n    i= 0\\n    while i < len(sentences):\\n        chunk = sentences[i]\\n        overlap = \\'\\'\\n        prev_len = 0\\n        prev = i - 1\\n        # 向前计算重叠部分\\n        while prev >= 0 and len(sentences[prev])+len(overlap) <= overlap_size:\\n            overlap = sentences[prev] + \\' \\' + overlap\\n            prev -= 1\\n        chunk = overlap+chunk\\n        next = i + 1\\n        # 向后计算当前chunk\\n        while next < len(sentences) and len(sentences[next])+len(chunk) <= chunk_size:\\n            chunk = chunk + \\' \\' + sentences[next]\\n            next += 1\\n        chunks.append(chunk)\\n        i = next\\n    return chunks\\n\\n\\n向量化（这里使用openai的向量化模型）\\ndef get_embedding(text, model=\"text-embedding-ada-002\"):\\n    \"\"\"封装 OpenAI 的 Embedding 模型接口\"\"\"\\n    return openai.Embedding.create(input=[text], model=model)[\\'data\\'][0][\\'embedding\\']\\n\\n\\n灌入向量库（使用chromadb）\\ndef __init__(self, name=\"demo\"):\\n        self.chroma_client = chromadb.Client(Settings(allow_reset=True))\\n        self.chroma_client.reset()\\n        self.name = name\\n        self.collection = self.chroma_client.get_or_create_collection(name=name)\\n\\ndef add_documents(self, documents):\\n        self.collection.add(\\n            embeddings=[get_embedding(doc) for doc in documents],\\n            documents=documents,\\n            metadatas=[{\"source\": self.name} for _ in documents],\\n            ids=[f\"id_{i}\" for i in range(len(documents))]\\n        )\\n\\n\\n检索向量数据库\\ndef search(self, query, top_n):\\n        \"\"\"检索向量数据库\"\"\"\\n        results = self.collection.query(\\n            query_embeddings=[get_embedding(query)],\\n            n_results=top_n\\n        )\\n        return results[\\'documents\\'][0]\\n\\n\\n将检索数据带入提示词\\ndef build_prompt(template=prompt_template, **kwargs):\\n    \"\"\"将 Prompt 模板赋值\"\"\"\\n    prompt = template\\n    for k, v in kwargs.items():\\n        if isinstance(v, str):\\n            val = v\\n        elif isinstance(v, list) and all(isinstance(elem, str) for elem in v):\\n            val = \\'\\\\n\\'.join(v)\\n        else:\\n            val = str(v)\\n        prompt = prompt.replace(f\"__{k.upper()}__\", val)\\n    return prompt\\n\\n\\n调用大模型\\ndef get_completion(prompt, context, model=\"gpt-3.5-turbo\"):\\n    \"\"\"封装 openai 接口\"\"\"\\n    messages = context + [{\"role\": \"user\", \"content\": prompt}]\\n    response = openai.ChatCompletion.create(\\n        model=model,\\n        messages=messages,\\n        temperature=0,  # 模型输出的随机性，0 表示随机性最小\\n    )\\n    return response.choices[0].message[\"content\"]\\n\\n\\n向量检索与关键字检索的对比\\n维度向量检索关键字检索检索方式基于文档和查询之间的相似度计算来进行检索。文档和查询通常被表示为高维空间中的向量，通过计算它们之间的相似度来确定最相关的文档。是通过匹配查询中的关键字与文档中的关键字来进行检索。当用户输入一个查询时，系统会在文档集合中查找包含这些关键字的文档，并将它们返回给用户。表示方式需要将文档和查询转换成向量形式，这通常通过词袋模型\\ue6ea或词嵌入\\ue6ea等技术实现，其中每个维度代表一个词汇项，而向量的值通常表示词频或TF-IDF权重。不需要对文档和查询进行特殊的表示，而是直接基于文档中的关键字与查询中的关键字进行匹配。匹配精度通常能够提供更精确的匹配，因为它考虑了文档和查询之间的语义相似度，而不仅仅是关键字的匹配。可能会受到一些问题的影响，例如同义词、拼写错误等，这可能会导致一些相关的文档被漏掉或者一些不相关的文档被检索到。使用范围通常在需要考虑语义相似度的情况下使用，例如在自然语言处理领域中的文档检索、语义搜索等方面。更适合简单的检索场景，例如在搜索引擎中用户输入关键字进行网页检索。\\n总的来说，向量检索更适合处理复杂的语义匹配问题，而_关键字检索则更适合简单的关键字匹配需求_。\\n如何学习AI大模型？\\n大模型时代，火爆出圈的LLM大模型让程序员们开始重新评估自己的本领。 “AI会取代那些行业？”“谁的饭碗又将不保了？”等问题热议不断。\\n不如成为「掌握AI工具的技术人」，毕竟AI时代，谁先尝试，谁就能占得先机！\\n想正式转到一些新兴的 AI 行业，不仅需要系统的学习AI大模型。同时也要跟已有的技能结合，辅助编程提效，或上手实操应用，增加自己的职场竞争力。\\n但是LLM相关的内容很多，现在网上的老课程老教材关于LLM又太少。所以现在小白入门就只能靠自学，学习成本和门槛很高\\n那么我作为一名热心肠的互联网老兵，我意识到有很多经验和知识值得分享给大家，希望可以帮助到更多学习大模型的人！至于能学习到多少就看你的学习毅力和能力了 。我已将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。\\n这份完整版的大模型 AI 学习资料已经上传CSDN，朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】 \\n👉 福利来袭CSDN大礼包：《2025最全AI大模型学习资源包》免费分享，安全可点 👈\\n全套AGI大模型学习大纲+路线\\nAI大模型时代的学习之旅：从基础到前沿，掌握人工智能的核心技能！\\n\\n640套AI大模型报告合集\\n这套包含640份报告的合集，涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师，还是对AI大模型感兴趣的爱好者，这套报告合集都将为您提供宝贵的信息和启示。\\n \\n \\n👉学会后的收获：👈 • 基于大模型全栈工程实现（前端、后端、产品经理、设计、数据分析等），通过这门课可获得不同能力；\\n• 能够利用大模型解决相关实际项目需求： 大数据时代，越来越多的企业和机构需要处理海量数据，利用大模型技术可以更好地处理这些数据，提高数据分析和决策的准确性。因此，掌握大模型应用开发技能，可以让程序员更好地应对实际项目需求；\\n• 基于大模型和企业数据AI应用开发，实现大模型理论、掌握GPU算力、硬件、LangChain开发框架和项目实战技能， 学会Fine-tuning垂直训练大模型（数据准备、数据蒸馏、大模型部署）一站式掌握；\\n• 能够完成时下热门大模型垂直领域模型训练能力，提高程序员的编码能力： 大模型应用开发需要掌握机器学习算法、深度学习框架等技术，这些技术的掌握可以提高程序员的编码能力和分析能力，让程序员更加熟练地编写高质量的代码。\\n👉 福利来袭CSDN大礼包：《2025最全AI大模型学习资源包》免费分享，安全可点 👈\\n\\n这份完整版的大模型 AI 学习资料已经上传CSDN，朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】  作为普通人，入局大模型时代需要持续学习和实践，不断提高自己的技能和认知水平，同时也需要有责任感和伦理意识，为人工智能的健康发展贡献力量。\\n\\n\\n\\n\\n\\n\\n')]\n"
     ]
    }
   ],
   "source": [
    "import bs4\n",
    "from langchain import hub\n",
    "from langchain_community.document_loaders import WebBaseLoader\n",
    "from langchain_core.documents import Document\n",
    "from langchain_text_splitters import RecursiveCharacterTextSplitter\n",
    "from langgraph.graph import START, StateGraph\n",
    "from typing_extensions import List, TypedDict\n",
    "\n",
    "# Load and chunk contents of the blog\n",
    "loader = WebBaseLoader(\n",
    "    web_paths=(\"https://lilianweng.github.io/posts/2023-06-23-agent/\",),\n",
    "    bs_kwargs=dict(\n",
    "        parse_only=bs4.SoupStrainer(\n",
    "            class_=(\"post-content\", \"post-title\", \"post-header\",\"blog-content-box\")\n",
    "        )\n",
    "    ),\n",
    ")\n",
    "docs = loader.load()\n",
    "print(docs)\n",
    "text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)\n",
    "all_splits = text_splitter.split_documents(docs)\n",
    "\n",
    "# Index chunks\n",
    "_ = vector_store.add_documents(documents=all_splits)\n",
    "\n",
    "# Define prompt for question-answering\n",
    "# N.B. for non-US LangSmith endpoints, you may need to specify\n",
    "# api_url=\"https://api.smith.langchain.com\" in hub.pull.\n",
    "prompt = hub.pull(\"rlm/rag-prompt\", api_url=\"https://api.smith.langchain.com\")\n",
    "\n",
    "\n",
    "# Define state for application\n",
    "class State(TypedDict):\n",
    "    question: str\n",
    "    context: List[Document]\n",
    "    answer: str\n",
    "\n",
    "\n",
    "# Define application steps\n",
    "def retrieve(state: State):\n",
    "    retrieved_docs = vector_store.similarity_search(state[\"question\"])\n",
    "    return {\"context\": retrieved_docs}\n",
    "\n",
    "\n",
    "def generate(state: State):\n",
    "    docs_content = \"\\n\\n\".join(doc.page_content for doc in state[\"context\"])\n",
    "    messages = prompt.invoke({\"question\": state[\"question\"], \"context\": docs_content})\n",
    "    response = llm.invoke(messages)\n",
    "    return {\"answer\": response.content}\n",
    "\n",
    "\n",
    "# Compile application and test\n",
    "graph_builder = StateGraph(State).add_sequence([retrieve, generate])\n",
    "graph_builder.add_edge(START, \"retrieve\")\n",
    "graph = graph_builder.compile()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "9b528972",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "input_variables=['context', 'question'] input_types={} partial_variables={} metadata={'lc_hub_owner': 'rlm', 'lc_hub_repo': 'rag-prompt', 'lc_hub_commit_hash': '50442af133e61576e74536c6556cefe1fac147cad032f4377b60c436e6cdcb6e'} messages=[HumanMessagePromptTemplate(prompt=PromptTemplate(input_variables=['context', 'question'], input_types={}, partial_variables={}, template=\"You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question. If you don't know the answer, just say that you don't know. Use three sentences maximum and keep the answer concise.\\nQuestion: {question} \\nContext: {context} \\nAnswer:\"), additional_kwargs={})]\n"
     ]
    }
   ],
   "source": [
    "print(prompt)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "7a2f5d880aaf31da",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "向量检索是一种基于文档和查询之间的相似度计算来进行检索的方法，通过将文档和查询表示为高维空间中的向量，并计算它们之间的相似度来确定最相关的文档。它通常使用词袋模型或词嵌入等技术将文本转换为向量形式，能够提供更精确的语义匹配。相比之下，关键字检索则直接匹配查询和文档中的关键字，更适合简单的检索场景。\n"
     ]
    }
   ],
   "source": [
    "response = graph.invoke({\"question\": \"什么是向量检索?\"})\n",
    "print(response[\"answer\"])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "2b94a68f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'retrieve': {'context': [Document(id='246f09d0-41bc-46cc-9e74-5de1c196f36d', metadata={'source': 'https://blog.csdn.net/python12222_/article/details/145327194'}, page_content='向量检索与关键字检索的对比\\n维度向量检索关键字检索检索方式基于文档和查询之间的相似度计算来进行检索。文档和查询通常被表示为高维空间中的向量，通过计算它们之间的相似度来确定最相关的文档。是通过匹配查询中的关键字与文档中的关键字来进行检索。当用户输入一个查询时，系统会在文档集合中查找包含这些关键字的文档，并将它们返回给用户。表示方式需要将文档和查询转换成向量形式，这通常通过词袋模型\\ue6ea或词嵌入\\ue6ea等技术实现，其中每个维度代表一个词汇项，而向量的值通常表示词频或TF-IDF权重。不需要对文档和查询进行特殊的表示，而是直接基于文档中的关键字与查询中的关键字进行匹配。匹配精度通常能够提供更精确的匹配，因为它考虑了文档和查询之间的语义相似度，而不仅仅是关键字的匹配。可能会受到一些问题的影响，例如同义词、拼写错误等，这可能会导致一些相关的文档被漏掉或者一些不相关的文档被检索到。使用范围通常在需要考虑语义相似度的情况下使用，例如在自然语言处理领域中的文档检索、语义搜索等方面。更适合简单的检索场景，例如在搜索引擎中用户输入关键字进行网页检索。\\n总的来说，向量检索更适合处理复杂的语义匹配问题，而_关键字检索则更适合简单的关键字匹配需求_。\\n如何学习AI大模型？\\n大模型时代，火爆出圈的LLM大模型让程序员们开始重新评估自己的本领。 “AI会取代那些行业？”“谁的饭碗又将不保了？”等问题热议不断。\\n不如成为「掌握AI工具的技术人」，毕竟AI时代，谁先尝试，谁就能占得先机！\\n想正式转到一些新兴的 AI 行业，不仅需要系统的学习AI大模型。同时也要跟已有的技能结合，辅助编程提效，或上手实操应用，增加自己的职场竞争力。\\n但是LLM相关的内容很多，现在网上的老课程老教材关于LLM又太少。所以现在小白入门就只能靠自学，学习成本和门槛很高\\n那么我作为一名热心肠的互联网老兵，我意识到有很多经验和知识值得分享给大家，希望可以帮助到更多学习大模型的人！至于能学习到多少就看你的学习毅力和能力了 。我已将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。\\n这份完整版的大模型 AI 学习资料已经上传CSDN，朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】'), Document(id='6b37f1a5-a9f1-4b96-ba7d-582fecd4cb6c', metadata={'source': 'https://blog.csdn.net/python12222_/article/details/145327194'}, page_content='向量数据库\\n向量数据库（Vector Database），也叫矢量数据库，主要用来存储和处理向量数据。\\n再结合刚才我们对向量定义的描述，图像、文本和音视频这种非结构化数据都可以通过某种变换或者嵌入学习转化为向量数据存储到向量数据库中，从而实现对图像、文本和音视频的相似性搜索和检索。\\n这意味着您可以使用向量数据库根据语义或上下文含义查找最相似或相关的数据，而不是使用基于精确匹配或预定义标准查询数据库的传统方法。也就是我们提到的关键字检索的局限性。\\n向量数据库的特点\\n这里我们为了方便使用向量数据库完成向量检索的方案，简单介绍下向量数据库的特点：\\n向量数据库的主要特点是高效存储与检索。利用索引技术和向量检索算法能实现高维大数据下的快速响应。向量数据库也是一种数据库，除了要管理向量数据外，还是支持对传统结构化数据的管理。实际使用时，有很多场景会同时对向量字段和结构化字段进行过滤检索，这对向量数据库来说也是一种挑战。\\n严格来说数据向量化本不属于向量数据库，但是数据向量化又是一项很重要的工作，为了流程的完整性暂且放进去。区别与传统数据库主要有以下几个地方不相同：数据向量化，向量检索和相似度计算。\\nchromadb的简单介绍\\n之所以介绍一下chromadb，下面我们的实战demo就是基于chromadb来实现。\\nChroma的目标是帮助用户更加便捷地构建大模型应用，更加轻松的将知识（knowledge）、事实（facts）和技能（skills）等我们现实世界中的文档整合进大模型中。\\nChroma提供的工具：\\n存储文档数据和它们的元数据：store embeddings and their metadata嵌入：embed documents and queries搜索： search embeddings\\n流向量数据库功能对比\\n由于大模型的火热，现在市面上的向量数据库众多，主流的向量数据库对比如下所示：'), Document(id='8651fddd-2008-43a6-b932-ca7cb4ac70f6', metadata={'source': 'https://blog.csdn.net/python12222_/article/details/145327194'}, page_content='文档切割（交叠切割防止问题的答案跨两个片段，使上下文更完整）\\ndef split_text(paragraphs,chunk_size=300,overlap_size=100):\\n    \"\"\"按指定 chunk_size 和 overlap_size 交叠割文本\"\"\"\\n    sentences = [s.strip() for p in paragraphs for s in sent_tokenize(p)]\\n    chunks = []\\n    i= 0\\n    while i < len(sentences):\\n        chunk = sentences[i]\\n        overlap = \\'\\'\\n        prev_len = 0\\n        prev = i - 1\\n        # 向前计算重叠部分\\n        while prev >= 0 and len(sentences[prev])+len(overlap) <= overlap_size:\\n            overlap = sentences[prev] + \\' \\' + overlap\\n            prev -= 1\\n        chunk = overlap+chunk\\n        next = i + 1\\n        # 向后计算当前chunk\\n        while next < len(sentences) and len(sentences[next])+len(chunk) <= chunk_size:\\n            chunk = chunk + \\' \\' + sentences[next]\\n            next += 1\\n        chunks.append(chunk)\\n        i = next\\n    return chunks'), Document(id='3957e51a-ee4b-4426-987e-edb8e35cedb3', metadata={'source': 'https://blog.csdn.net/python12222_/article/details/145327194'}, page_content='文本向量是怎么得到的\\n构建相关（正立）与不相关（负例）的句子对儿样本训练双塔式模型，让正例间的距离小，负例间的距离大\\n\\n向量间的相似度计算\\n\\n我们用检索关键词和一组文本的样例来看下效果\\n余弦距离和欧氏距离的核心逻辑\\ndef cos_sim(a, b):\\n    \\'\\'\\'余弦距离 -- 越大越相似\\'\\'\\'\\n    return dot(a, b)/(norm(a)*norm(b))\\n\\ndef l2(a, b):\\n    \\'\\'\\'欧氏距离 -- 越小越相似\\'\\'\\'\\n    x = np.asarray(a)-np.asarray(b)\\n    return norm(x)\\n    \\ndef get_embeddings(texts, model=\"text-embedding-ada-002\", dimensions=None):\\n    \\'\\'\\'封装 OpenAI 的 Embedding 模型接口\\'\\'\\'\\n    if model == \"text-embedding-ada-002\":\\n        dimensions = None\\n    if dimensions:\\n        data = client.embeddings.create(\\n            input=texts, model=model, dimensions=dimensions).data\\n    else:\\n        data = client.embeddings.create(input=texts, model=model).data\\n    return [x.embedding for x in data]\\n    \\n# query = \"国际争端\"\\n\\n# 且能支持跨语言\\nquery = \"global conflicts\"\\n\\ndocuments = [\\n    \"联合国就苏丹达尔富尔地区大规模暴力事件发出警告\",\\n    \"土耳其、芬兰、瑞典与北约代表将继续就瑞典“入约”问题进行谈判\",\\n    \"日本岐阜市陆上自卫队射击场内发生枪击事件 3人受伤\",\\n    \"国家游泳中心（水立方）：恢复游泳、嬉水乐园等水上项目运营\",\\n    \"我国首次在空间站开展舱外辐射生物学暴露实验\",\\n]')]}}\n",
      "\n",
      "----------------\n",
      "\n",
      "{'generate': {'answer': \"Task decomposition is not directly discussed in the provided context. I don't know the answer based on the given information.\"}}\n",
      "\n",
      "----------------\n",
      "\n"
     ]
    }
   ],
   "source": [
    "for step in graph.stream(\n",
    "    {\"question\": \"What is Task Decomposition?\"}, stream_mode=\"updates\"\n",
    "):\n",
    "    print(f\"{step}\\n\\n----------------\\n\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "e99b8929",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "|向量|检索|是一种|基于|文档|和|查询|之间的|相似|度|计算|来进行|检索|的方法|，|通过|将|文档|和|查询|表示为|高|维|空间|中的|向量|，|并|计算|它们|之间的|相似|度|来确定|最|相关的|文档|。|与|关键字|检索|相比|，|向量|检索|能|更好地|处理|语义|相似|度|问题|，|适合|复杂的|语义|匹配|场景|。||"
     ]
    }
   ],
   "source": [
    "for message, metadata in graph.stream(\n",
    "    {\"question\": \"什么是向量检索?\"}, stream_mode=\"messages\"\n",
    "):\n",
    "    print(message.content, end=\"|\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "langchain",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
